package com.tencent.ncnnyolox.basemodule.base

import android.app.Application
import android.content.Context
import android.net.ConnectivityManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.Utils
import com.tencent.ncnnyolox.basemodule.event.Message
import com.tencent.ncnnyolox.basemodule.event.SingleLiveEvent
import com.yd.basemodule.base.IBaseResponse
import com.yd.basemodule.error.ResponseThrowable
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.flow

/**
 * <pre>
 *     author : yh
 *     e-mail : xxx@xx
 *     time   : 2023/12/22
 *     desc   :
 * </pre>
 */
open class BaseViewModel(application: Application = Utils.getApp()) : AndroidViewModel(application),
    LifecycleObserver {

    //lazy用于懒加载的单例 lateinit 只用于变量 var，而 lazy 只用于常量 val
    val defUI: UIChange by lazy { UIChange() }

    /**
     * 所有网络请求都在viewModelScope域中启动,当页面销毁是会自动
     * 调用ViewModel  #onCleard取消所有协程
     */
    fun launchUI(block: suspend CoroutineScope.() -> Unit) {
        LogUtils.d("BaseViewModel","是否可用${isNetworkConnected(getApplication())}")

        if (!isNetworkConnected(getApplication())){
            defUI.toastEvent.postValue("请检查网络状态")
            return
        }
        defUI.showDialog.call()
        viewModelScope.launch {
            kotlin.runCatching {
                block()
            }.onSuccess {
                defUI.dismissDialog.call()
                LogUtils.d("BaseViewModel-launchUI","请求成功")
            }.onFailure {
                defUI.dismissDialog.call()
                defUI.toastEvent.postValue(AppConfig.getConfig().globalExceptionHandle(it).errMsg)
                LogUtils.d("BaseViewModel-launchUI","请求失败---"+it.message)
                it.printStackTrace()
            }
        }
    }

    /**
     * 用流的方式进行网络请求
     */
    fun <T> launchFlow(block: suspend () -> T): kotlinx.coroutines.flow.Flow<T> {
        return flow {
            emit(block())
        }
    }

    /**
     * 网络连接是否正常
     *
     * @return true:有网络    false:无网络
     */
    fun isNetworkConnected(context: Context):Boolean {

        if (context!=null){
            val mConnectivityManager: ConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager;
            val mNetworkInfo = mConnectivityManager.getActiveNetworkInfo()
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable();
            }
        }
        return false
    }

//    fun isNetworkOnline():Boolean{
//        val runtime=Runtime.getRuntime()
//        try {
//            val ipProcess=runtime.exec("ping -c 3 www.baidu.com")
//            val exitValue=ipProcess.waitFor()
//            LogUtils.d("BaseViewModel","process: "+exitValue);
//            return (exitValue == 0)
//        }catch (e:Exception){
//            e.printStackTrace()
//        }
//        return false
//    }

    /**
     *  不过滤请求结果
     * @param block 请求体
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun launchGo(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit = {
            defUI.toastEvent.postValue("${it.code}:${it.errMsg}")
        },
        complete: suspend CoroutineScope.() -> Unit = {},
        isShowDialog: Boolean = true
    ) {
        LogUtils.d("BaseViewModel","是否可用${isNetworkConnected(getApplication())}"+"-----")
        if (!isNetworkConnected(getApplication())){
            defUI.toastEvent.postValue("请检查网络状态")
            return
        }
        if (isShowDialog) defUI.showDialog.call()
//        launchUI {
//            handleException(
//                withContext(Dispatchers.IO) { block },
//                { error(it) },
//                {
//                    defUI.dismissDialog.call()
//                    complete()
//                }
//            )
//        }
        viewModelScope.launch {
            kotlin.runCatching {
                block()
            }.onSuccess {
                defUI.dismissDialog.call()
                LogUtils.d("BaseViewModel-launchGo","请求成功")
            }.onFailure {
               defUI.toastEvent.postValue(AppConfig.getConfig().globalExceptionHandle(it).errMsg)
                LogUtils.d("BaseViewModel-launchGo","请求失败---"+it.message)
                it.printStackTrace()
            }
        }
    }

    /**
     * 过滤请求结果，其他全抛异常
     * @param block 请求体
     * @param success 成功回调
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun <T> launchOnlyresult(
        block: suspend CoroutineScope.() -> IBaseResponse<T>,
        success: (T) -> Unit,
        error: (ResponseThrowable) -> Unit = {
            defUI.toastEvent.postValue("${it.code}:${it.errMsg}")
        },
        complete: () -> Unit = {},
        isShowDialog: Boolean = true
    ) {
        if (!isNetworkConnected(getApplication())){
            defUI.toastEvent.postValue("请检查网络状态")
            return
        }
        if (isShowDialog) defUI.showDialog.call()
        launchUI {
            handleException(
                {
                    withContext(Dispatchers.IO) {
                        block().let {
                            if (it.isSuccess()) it.data()
                            else throw ResponseThrowable(it.code(), it.msg())
                        }
                    }.also { success(it) }
                },
                { error(it) },
                {
                    defUI.dismissDialog.call()
                    complete()
                }
            )
        }
    }


    /**
     * 异常统一处理
     */
    private suspend fun handleException(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
        complete: suspend CoroutineScope.() -> Unit
    ) {
        coroutineScope {
            try {
                block()
            } catch (e: Throwable) {
                error(AppConfig.getConfig().globalExceptionHandle(e))
            } finally {
                complete()
            }
        }
    }

    /**
     * UI事件
     */
    inner class UIChange {
        val showDialog by lazy { SingleLiveEvent<String>() }
        val dismissDialog by lazy { SingleLiveEvent<Void>() }
        val toastEvent by lazy { SingleLiveEvent<String>() }
        val msgEvent by lazy { SingleLiveEvent<Message>() }
    }

}